home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
mxcode
/
tnypl211
/
modplay.asm
< prev
next >
Wrap
Assembly Source File
|
1994-06-21
|
63KB
|
2,097 lines
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
; Tiny MOD Player for Watcom C/C++32 and DOS/4GW
; Version 2.11a June 5th, 1994
;
; Copyright 1993,94 Carlos Hasan
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
ideal
p386
model flat,c
smart
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; EQUATES AND PUBLICS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
MAXVOICES = 8 ; number of voices
DMABUFLEN = 1024 ; DMA buffer length (multiple of 64)
VOLBUFLEN = 66*256 ; volume table length
MIXBUFLEN = 2*DMABUFLEN+2048 ; mixing/boosting buffer length
TIMERRATE = 17000 ; timer interrupt rate in ticks
global MODPlayModule:proc
global MODStopModule:proc
global MODPlaySample:proc
global MODStopSample:proc
global MODSetPeriod:proc
global MODSetVolume:proc
global MODSetMusicVolume:proc
global MODSetSampleVolume:proc
global MODDetectCard:proc
global MODPoll:proc
global MODVoiceTable:dword
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; STRUCTURES
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
struc module ; module structure
numtracks dw ? ; number of tracks
orderlen dw ? ; order length
orders db 128 dup (?) ; order list
patterns dd 128 dup (?) ; pattern addresses
sampptr dd 32 dup (?) ; sample start addresses
sampend dd 32 dup (?) ; sample end addresses
samploop dd 32 dup (?) ; sample loop point addresses
sampvolume db 32 dup (?) ; sample default volumes
ends module
struc sample ; sample structure
period dw ? ; default period
volume dw ? ; default volume
datalen dd ? ; sample data length
dataptr dd ? ; sample data address
ends sample
struc track ; track structure
note dw ? ; note index
period dw ? ; period value
inst db ? ; instrument
volume db ? ; volume
effect dw ? ; effect
destperiod dw ? ; toneporta wanted period
tonespeed db ? ; toneporta speed
vibparm db ? ; vibrato depth/rate
vibpos db ? ; vibrato wave position
tremparm db ? ; tremolo depth/rate
trempos db ? ; tremolo wave position
db ? ; alignment
arptable dw 3 dup (?) ; arpeggio periods
ends track
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; DATA
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Module Player data
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
udataseg
moduleptr dd ? ; current module address
pattptr dd ? ; current playing pattern address
orderpos db ? ; order position
orderlen db ? ; order length
pattrow db ? ; pattern row
tempo db ? ; tempo
tempocount db ? ; tempo counter
bpm db ? ; beats per minute
musicvolume db ? ; music channels volume
samplevolume db ? ; sample channels volume
numtracks dw ? ; number of tracks
tracks track MAXVOICES dup (?)
pitchtable dd 3425 dup (?) ; period to pitch table
; Amiga period table
dataseg
periodtable dw 0
dw 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812
dw 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
dw 856,808,762,720,678,640,604,570,538,508,480,453
dw 428,404,381,360,339,320,302,285,269,254,240,226
dw 214,202,190,180,170,160,151,143,135,127,120,113
dw 107,101,95,90,85,80,75,71,67,63,60,56
dw 53,50,47,45,42,40,37,35,33,31,30,28
; Sinus wave table
sintable db 0,25,50,74,98,120,142,162,180,197,212,225
db 236,244,250,254,255,254,250,244,236,225
db 212,197,180,162,142,120,98,74,50,25
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster driver data
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
udataseg
; Voices programmable parameters
label MODVoiceTable dword
voicepos dd MAXVOICES dup (?)
voiceend dd MAXVOICES dup (?)
voiceloop dd MAXVOICES dup (?)
voicefrac dd MAXVOICES dup (?)
voicepitch dd MAXVOICES dup (?)
voicevolume dd MAXVOICES dup (?)
; Internal driver data
dataseg
mixbuffer dd ? ; mixing buffer address
boosttable dd ? ; boosting table address
voltable dd ? ; volume table address
numvoices dw ? ; number of active voices
mixfreq dw ? ; playback frequency
ioaddr dw ? ; card I/O port address
irqnum db ? ; card IRQ level
drqnum db ? ; card DMA channel
timerproc dd ? ; timer callback address
timeracc dd ? ; timer callback accumulator
timerspeed dd ? ; timer callback speed
datasel dw ? ; flat model data selector
bufsel dw ? ; DOS memory block selector
bufptr dd ? ; DMA buffer address
bufoff dd ? ; double buffer offset
oldirqoff dd ? ; old IRQ vector address
oldirqsel dw ?
oldtimeroff dd ? ; old timer IRQ0 vector address
oldtimersel dw ?
oldtimeracc dw ? ; old timer accumulator
manualmode db ? ; timer/manual polling mode
playing db 0 ; playing/stopped status
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; CODE
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
codeseg
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Copyright Strings
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
db 'Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan',0
db 'Compiled on: ',??date,' ',??time,0
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Module Player stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPlayModule - start playing a music module
; In:
; Song = module address
; Chans = number of channels
; Rate = playback rate
; Port = port address
; irq = irq number
; dma = dma channel
; mode = polling mode
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPlayModule Song:dword,Chans:dword,Rate:dword,Port:dword,IRQ:dword,DRQ:dword,Mode:dword
pushad
; setup the music module address
mov esi,[Song]
mov [moduleptr],esi
; setup the sound card driver
mov ax,[word Rate]
mov bl,[byte Chans]
mov dx,[word Port]
mov cl,[byte IRQ]
mov ch,[byte DRQ]
mov bh,[byte Mode]
call mixinit
jc playmoduled0
; build the period to pitch table (16.16 fixed point values)
movzx ebx,ax
mov eax,8363*428
xor edx,edx
shld edx,eax,16
shl eax,16
div ebx
mov esi,eax
lea edi,[pitchtable]
mov ecx,3425
xor ebx,ebx
playmodulel0:
inc ebx
xor edx,edx
mov eax,esi
div ebx
mov [edi],eax
add edi,4
loop playmodulel0
; setup global volumes for music and sample channels
mov [musicvolume],255
mov [samplevolume],255
; clear the module player track structures
push es
mov ax,ds
mov es,ax
cld
lea edi,[tracks]
mov ecx,MAXVOICES*(size track)
xor al,al
rep stosb
pop es
; check if there is a module to playback
xor eax,eax
mov [numtracks],ax
mov esi,[moduleptr]
test esi,esi
clc
je playmoduled0
; setup player interpreter variables
mov esi,[moduleptr]
mov ax,[esi+module.orderlen]
mov [orderlen],al
mov ax,[esi+module.numtracks]
mov [numtracks],ax
mov [tempo],6
mov [bpm],125
mov [orderpos],0
mov [tempocount],0
mov [pattrow],40h
; setup the player callback timer routine
lea edx,[pollmodule]
call mixsettimerproc
mov dl,[bpm]
call mixstarttimer
clc
playmoduled0:
popad
sbb eax,eax
ret
endp MODPlayModule
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODStopModule - shut down the music system
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODStopModule
pushad
call mixstoptimer ; stop the timer callback
call mixdone ; shutdown the SB stuff
popad
ret
endp MODStopModule
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPoll - polls the music system in manual mode
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPoll
pushad
cmp [manualmode],0 ; call the polling routine only
je modpolld0 ; if we are using the manual
cmp [playing],0 ; polling mode (and if the driver
je modpolld0 ; is active).
call mixpoll
modpolld0:
popad
ret
endp MODPoll
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODPlaySample - play sample instrument
; In:
; voice = voice number
; sample = sample address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODPlaySample Voice:dword,SamplePtr:dword
pushad
cli
; get the voice number and track address
mov ebx,[Voice]
mov edi,[SamplePtr]
mov esi,ebx
imul esi,size track
; set the voice pitch value
movzx eax,[edi+sample.period]
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
; set the voice sample parameters
mov eax,[edi+sample.dataptr]
mov [4*ebx+voicepos],eax
add eax,[edi+sample.datalen]
mov [4*ebx+voiceend],eax
mov [4*ebx+voiceloop],eax
; set the voice and track volumes
mov ax,[edi+sample.volume]
mov [esi+tracks.volume],al
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
sti
popad
ret
endp MODPlaySample
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODStopSample - stop the playing sample
; In:
; voice = voice number
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODStopSample Voice:dword
pushad
cli
; get the voice number
mov ebx,[Voice]
xor eax,eax
; clear the voice sample parameters
mov [4*ebx+voicepos],eax
mov [4*ebx+voiceend],eax
mov [4*ebx+voiceloop],eax
sti
popad
ret
endp MODStopSample
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetPeriod - set the voice period value
; In:
; voice = voice number
; period = period value (113-856)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetPeriod Voice:dword,Period:dword
pushad
cli
; get the voice number and period value
mov ebx,[Voice]
mov eax,[Period]
; set the voice pitch value
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
sti
popad
ret
endp MODSetPeriod
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetVolume - set the voice volume level
; In:
; voice = voice number
; volume = volume level (0-64)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetVolume Voice:dword,Volume:dword
pushad
cli
; get the voice number and track address
mov ebx,[Voice]
mov eax,[Volume]
mov esi,ebx
imul esi,size track
; set the voice and track volume
mov [esi+tracks.volume],al
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
sti
popad
ret
endp MODSetVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetMusicVolume - set the global music volume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetMusicVolume Volume:dword
pushad
cli
; set new music volume
mov eax,[Volume]
mov [musicvolume],al
; update all the music voices
lea esi,[tracks]
xor ebx,ebx
setmusicvolumel0:
mov al,[esi+track.volume]
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
add esi,size track
inc ebx
cmp bx,[numtracks]
jb setmusicvolumel0
sti
popad
ret
endp MODSetMusicVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODSetSampleVolume - set the global sample volume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODSetSampleVolume Volume:dword
pushad
cli
; set the sample volume
mov eax,[Volume]
mov [samplevolume],al
; update all the sample voices
lea esi,[tracks]
xor ebx,ebx
setsamplevolumel0:
cmp bx,[numtracks]
jb setsamplevolumef0
mov al,[esi+track.volume]
mul [samplevolume]
mov [byte 4*ebx+voicevolume],ah
setsamplevolumef0:
add esi,size track
inc ebx
cmp bx,MAXVOICES
jb setsamplevolumel0
sti
popad
ret
endp MODSetSampleVolume
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; MODDetectCard - detect the Sound Blaster configuration
; Out:
; Port = I/O Port
; IRQ = IRQ level
; DRQ = DMA channel
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
proc MODDetectCard Port:dword,IRQ:dword,DRQ:dword
pushad
; call the lowlevel autodetection routine
call mixdetect
; set the parameters in the user variables
mov eax,[Port]
mov [eax],dx
mov eax,[IRQ]
mov [eax],cl
mov eax,[DRQ]
mov [eax],ch
popad
sbb eax,eax
ret
endp MODDetectCard
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; pollmodule - polls the module player
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
pollmodule:
pushad
dec [tempocount] ; decrease the tempo counter
jle pollmodulef0
lea esi,[tracks] ; while in the same pattern row
xor ebx,ebx ; update the track effects.
pollmodulel0:
call updatechannel
add esi,size track
inc ebx
cmp bx,[numtracks]
jb pollmodulel0
popad
ret
pollmodulef0: ; advance to the next pattern row.
mov al,[tempo] ; update the tempo counter
mov [tempocount],al
mov edx,[moduleptr] ; get the module and pattern address
mov edi,[pattptr]
cmp [pattrow],40h ; need to advance to the next order?
jb pollmodulef2
xor eax,eax ; reset the pattern row
mov [pattrow],al
mov al,[orderpos] ; if we are at the end of the order
cmp al,[orderlen] ; list, loop to the beginning
jb pollmodulef1
xor al,al
mov [orderpos],al
pollmodulef1:
inc [orderpos] ; get the new pattern address
movzx eax,[edx+eax+module.orders]
mov edi,[edx+4*eax+module.patterns]
pollmodulef2:
inc [pattrow] ; increase pattern row number
lea esi,[tracks]
xor ebx,ebx ; read and interpret the next
pollmodulel1: ; pattern row of events
call readchannel
add esi,size track
add edi,4
inc ebx
cmp bx,[numtracks]
jb pollmodulel1
mov [pattptr],edi ; save pattern row address
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; readchannel - read the next note event from the pattern sheet
; In:
; EBX = voice number
; ESI = track address
; EDI = pattern address
; EDX = module address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
readchannel:
pushad
; check for new sample number . . .
mov al,[edi+1]
test al,al
je readchannelf0
mov [esi+track.inst],al
movzx eax,al
mov al,[edx+eax+module.sampvolume]
mov [esi+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
; check for new note pitch . . .
readchannelf0:
mov al,[edi]
test al,al
je readchannelf1
movzx eax,al
mov [esi+track.note],ax
cmp [byte edi+3],03h
je readchannelf1
mov ax,[2*eax+periodtable]
mov [esi+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
movzx eax,[esi+track.inst]
lea edx,[4*eax+edx]
mov eax,[edx+module.sampptr]
mov [4*ebx+voicepos],eax
mov eax,[edx+module.sampend]
mov [4*ebx+voiceend],eax
mov eax,[edx+module.samploop]
mov [4*ebx+voiceloop],eax
; check the new track effect . . .
readchannelf1:
mov dx,[edi+2]
mov [esi+track.effect],dx
movzx eax,dh
and al,0Fh
call [4*eax+efxtable]
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; updatechannel - update the track using the current effect
; In:
; EBX = voice number
; ESI = track address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
updatechannel:
pushad
mov dx,[esi+track.effect]
movzx eax,dh
and al,0Fh
call [4*eax+efxtable2]
popad
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Protracker effects stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Effect jump tables
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
align 4
label efxtable dword
dd efxarpeggio ; 0 - arpeggio
dd efxnull ; 1 - porta up
dd efxnull ; 2 - porta down
dd efxtoneporta ; 3 - tone porta
dd efxvibrato ; 4 - vibrato
dd efxnull ; 5 - tone+slide
dd efxnull ; 6 - vibrato+slide
dd efxtremolo ; 7 - tremolo
dd efxnull ; 8 - unused
dd efxsampoffset ; 9 - sample offset
dd efxnull ; A - volume slide
dd efxpattjump ; B - pattern jump
dd efxsetvolume ; C - set volume
dd efxbreak ; D - break pattern
dd efxnull ; E - extra effects
dd efxsetspeed ; F - set speed
label efxtable2 dword
dd efxarpeggio2 ; 0 - arpeggio
dd efxportaup ; 1 - porta up
dd efxportadown ; 2 - porta down
dd efxtoneporta2 ; 3 - tone porta
dd efxvibrato2 ; 4 - vibrato
dd efxtoneslide ; 5 - tone+slide
dd efxvibslide ; 6 - vibrato+slide
dd efxtremolo2 ; 7 - tremolo
dd efxnull ; 8 - unused
dd efxnull ; 9 - sample offset
dd efxvolslide ; A - volume slide
dd efxnull ; B - pattern jump
dd efxnull ; C - set volume
dd efxnull ; D - break pattern
dd efxnull ; E - extra effects
dd efxnull ; F - set speed
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxnull - dummy effect
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxnull:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxarpeggio - arpeggio
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxarpeggio:
test dl,dl
je efxnull
mov dh,dl
and dl,0Fh
shr dh,4
movzx eax,[esi+track.note]
mov cx,[2*eax+periodtable]
mov [esi+track.arptable],cx
add al,dh
mov cx,[2*eax+periodtable]
mov [esi+2+track.arptable],cx
sub al,dh
add al,dl
mov cx,[2*eax+periodtable]
mov [esi+4+track.arptable],cx
ret
efxarpeggio2:
test dl,dl
je efxnull
movzx eax,[esi+track.arptable]
xchg [esi+4+track.arptable],ax
xchg [esi+2+track.arptable],ax
mov [esi+track.arptable],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxportaup - slides the pitch up
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxportaup:
xor dh,dh
movzx eax,[esi+track.period]
sub ax,dx
cmp ax,28
jge efxportaupf0
mov ax,28
efxportaupf0:
mov [esi+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxportadown - slides the pitch down
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxportadown:
xor dh,dh
movzx eax,[esi+track.period]
add ax,dx
cmp ax,3424
jle efxportadownf0
mov ax,3424
efxportadownf0:
mov [esi+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtoneporta - tone portamento
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtoneporta:
test dl,dl
jne efxtoneportaf0
mov dl,[esi+track.tonespeed]
efxtoneportaf0:
mov [esi+track.tonespeed],dl
mov [esi+track.effect],dx
movzx eax,[esi+track.note]
mov ax,[2*eax+periodtable]
mov [esi+track.destperiod],ax
ret
efxtoneporta2:
xor dh,dh
movzx eax,[esi+track.period]
mov cx,[esi+track.destperiod]
cmp ax,cx
je efxnull
jg efxtoneportaf1
add ax,dx
cmp ax,cx
jle efxtoneportaf2
mov ax,cx
efxtoneportaf2:
mov [esi+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
efxtoneportaf1:
sub ax,dx
cmp ax,cx
jge efxtoneportaf3
mov ax,cx
efxtoneportaf3:
mov [esi+track.period],ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvibrato - pitch vibrato
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvibrato:
mov al,[esi+track.vibparm]
mov ah,al
and ax,0F00Fh
test dl,0Fh
jne efxvibratof0
or dl,al
efxvibratof0:
test dl,0F0h
jne efxvibratof1
or dl,ah
efxvibratof1:
mov [esi+track.vibparm],dl
mov [esi+track.effect],dx
ret
efxvibrato2:
mov dh,dl
and dx,0F00Fh
shr dh,2
mov al,[esi+track.vibpos]
add [esi+track.vibpos],dh
mov dh,al
shr al,2
and eax,1Fh
mov al,[eax+sintable]
mul dl
shr ax,7
test dh,dh
jge efxvibratof2
neg ax
efxvibratof2:
add ax,[esi+track.period]
cmp ax,28
jge efxvibratof3
mov ax,28
efxvibratof3:
cmp ax,3424
jle efxvibratof4
mov ax,3424
efxvibratof4:
movzx eax,ax
mov eax,[4*eax+pitchtable]
mov [4*ebx+voicepitch],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtoneslide - volume slide and continue last portamento
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtoneslide:
call efxvolslide
mov dl,[esi+track.tonespeed]
jmp efxtoneporta
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvibslide - volume slide and continue last pitch vibrato
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvibslide:
call efxvolslide
mov dl,[esi+track.vibparm]
jmp efxvibrato2
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxtremolo - volume vibrato
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxtremolo:
mov al,[esi+track.tremparm]
mov ah,al
and ax,0F00Fh
test dl,0Fh
jne efxtremolof0
or dl,al
efxtremolof0:
test dl,0F0h
jne efxtremolof1
or dl,ah
efxtremolof1:
mov [esi+track.tremparm],dl
mov [esi+track.effect],dx
ret
efxtremolo2:
mov dh,dl
and dx,0F00Fh
shr dh,2
mov al,[esi+track.trempos]
add [esi+track.trempos],dh
mov dh,al
shr al,2
and eax,1Fh
mov al,[eax+sintable]
mul dl
shr ax,6
test dh,dh
jge efxtremolof2
neg ax
efxtremolof2:
add al,[esi+track.volume]
jge efxtremolof3
xor al,al
efxtremolof3:
cmp al,40h
jle efxtremolof4
mov al,40h
efxtremolof4:
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsampoffset - set the sample offset
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsampoffset:
movzx eax,[esi+track.inst]
mov esi,[moduleptr]
mov eax,[esi+4*eax+module.sampptr]
movzx edx,dl
shl edx,8
add eax,edx
mov [4*ebx+voicepos],eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxvolslide - volume slide
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxvolslide:
mov al,[esi+track.volume]
mov dh,dl
shr dl,4
je efxvolslidef0
add al,dl
cmp al,40h
jle efxvolslidef1
mov al,40h
efxvolslidef1:
mov [esi+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
efxvolslidef0:
sub al,dh
jge efxvolslidef2
xor al,al
efxvolslidef2:
mov [esi+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxpattjump - jump to order pattern
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxpattjump:
mov [orderpos],dl
mov [pattrow],40h
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsetvolume - set volume
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsetvolume:
mov al,dl
mov [esi+track.volume],al
mul [musicvolume]
mov [byte 4*ebx+voicevolume],ah
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxbreak - break pattern
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxbreak:
mov [pattrow],40h
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; efxsetspeed - set the tempo or BPM speed
; In:
; EBX = voice number
; ESI = track address
; DL = effect parameter
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
efxsetspeed:
test dl,dl
je efxnull
cmp dl,20h
jae efxsetbpm
mov [tempo],dl
mov [tempocount],dl
ret
efxsetbpm:
mov [bpm],dl
call mixstarttimer
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster Driver highlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixinit - initialize the sound driver
; In:
; AX = mixing speed in hertz
; BL = number of voices
; DX = I/O port address
; CL = IRQ level
; CH = DRQ channel
; BH = polling mode
; Out:
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixinit:
pushad
cmp [playing],0
stc
jne mixinitd0
; setup sound card parameters
mov [manualmode],bh
xor bh,bh
mov [mixfreq],ax
mov [numvoices],bx
mov [ioaddr],dx
mov [irqnum],cl
mov [drqnum],ch
; check if the sound card is present
call sbreset
jc mixinitd0
; setup timer and double buffer variables
mov [timerproc],offset nulltimer
xor eax,eax
mov [bufoff],eax
mov [timeracc],eax
mov [timerspeed],256
; clear voice parameters
push es
mov ax,ds
mov es,ax
cld
lea edi,[voicepos]
mov ecx,6*MAXVOICES
xor eax,eax
rep stosd
pop es
; allocate conventional memory for the DMA buffer, the volume table,
; the mixing buffer and the boosting table.
mov ax,0100h
mov bx,(DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15)/16
int 31h
jc mixinitd0
mov [bufsel],dx
movzx eax,ax
shl eax,4
; set the address of the mixing buffer and boosting table
mov [mixbuffer],eax
add eax,2*DMABUFLEN
mov [boosttable],eax
add eax,2048
; get the address of the DMA buffer and volume table
mov ecx,DMABUFLEN
lea edx,[eax+ecx]
; check for cross-pages in the DMA buffer and align the Volume table
mov esi,eax
add si,cx
jnc mixinitf0
mov edx,eax
add eax,VOLBUFLEN
mixinitf0:
add edx,255
xor dl,dl
mov [bufptr],eax
mov [voltable],edx
; clear DMA buffer with centered samples
push es
mov ax,ds
mov es,ax
cld
mov edi,[bufptr]
mov ecx,DMABUFLEN
mov al,80h
rep stosb
pop es
; build volume table and boosting table
mov cl,6
mov edi,[voltable]
xor bx,bx
mixinitl0:
mov al,bl
imul bh
sar ax,cl
mov [edi],al
inc edi
inc bl
jne mixinitl0
inc bh
cmp bh,40h
jbe mixinitl0
push es
mov ax,ds
mov es,ax
cld
mov edi,[boosttable]
mov ecx,768
xor ax,ax
rep stosb
mov ecx,512
mixinitl1:
mov [edi],ah
add ax,80h
inc edi
loop mixinitl1
mov ecx,768
dec al
rep stosb
pop es
; initialize the sound card for output
call dmasetup
call irqsetup
call sbsetup
; dont use the timer interrupt for manual polling mode
cmp [manualmode],0
jne mixinitf1
; install timer interrupt to poll the driver
push es
mov ax,cs
mov es,ax
lea ebx,[mixtimer]
mov cl,0
call irqsetvect
mov [oldtimeroff],ebx
mov [oldtimersel],es
pop es
; set the timer frequency to 70 hertz
cli
mov al,36h
out 43h,al
mov ax,TIMERRATE
out 40h,al
mov al,ah
out 40h,al
sti
; set driver playing status
mixinitf1:
mov [playing],1
clc
mixinitd0:
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixdone - deinitialize the sound driver
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixdone:
pushad
cmp [playing],1
jne mixdoned0
; the timer interrupt was modified if we are using the timer polling mode
cmp [manualmode],0
jne mixdonef0
; restore the timer frequency to 18.2 hertz
cli
mov al,36h
out 43h,al
xor al,al
out 40h,al
out 40h,al
sti
; deinstall timer interrupt used to poll the driver
push es
mov ebx,[oldtimeroff]
mov es,[oldtimersel]
mov cl,0
call irqsetvect
pop es
; deinitialize the sound card output
mixdonef0:
call sbdone
call irqdone
call dmadone
; free conventional memory block used for DMA buffer, volume table,
; mixing buffer and boosting table.
mov ax,0101h
mov dx,[bufsel]
int 31h
; set driver stopped status
mov [playing],0
mixdoned0:
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixtimer - timer interrupt routine used to poll the sound driver
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixtimer:
push eax
push ds
mov ds,[cs:datasel]
call mixpoll ; poll the sound system
add [oldtimeracc],TIMERRATE
jnc mixtimerf0 ; time to call the old IRQ0 vector?
pop ds ; yes, jump to the old IRQ0 service
pop eax
jmp [fword cs:oldtimeroff]
mixtimerf0:
mov al,20h ; nope, send PIC acknowledge and exit
out 20h,al
pop ds
pop eax
iretd
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixdetect - detect the sound card configuration
; Out:
; DX = I/O Port
; CL = IRQ level
; CH = DMA channel
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixdetect:
cmp [playing],1 ; do not try to autodetect
stc ; if we are already playing
je mixdetectd0
call sbdetect
mixdetectd0:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixsettimerproc - set the timer procedure
; In:
; EDX = timer routine address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixsettimerproc:
mov [timerproc],edx ; set the timer callback address
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixstarttimer - start the timer at the specified speed
; In:
; DL = timer speed in beats per minute (BPMs)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixstarttimer:
push eax
push ebx
push edx
mov bh,dl ; set the timer callback speed
xor bl,bl ; to 24/60*BPM hertz
mov ax,[mixfreq]
mov dx,0280h
mul dx
div bx
movzx eax,ax
mov [timerspeed],eax
pop edx
pop ebx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixstoptimer - stop the timer routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixstoptimer:
mov [timerproc],offset nulltimer
nulltimer:
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixvoice - mixes the voice samples
; In:
; EBX = voice number (*4)
; ECX = number of samples
; EDI = buffer address
; Out:
; EDI = buffer end address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixvoice:
macro mixcode OPCODE
local mixcodel0,mixjmptable
push eax
push ebx
push ecx
push edx
push ebp
push esi
push ebx
mov esi,[ebx+voicepos]
mov dl,[byte ebx+3+voicefrac]
mov dh,[byte ebx+1+voicepitch]
movzx ebp,[word ebx+2+voicepitch]
mov ebx,[ebx+voicevolume]
mov bh,bl
add ebx,[voltable]
movzx eax,cl
and al,31
shr ecx,5
lea edi,[edi+4*eax-4*32]
jmp [4*eax+mixjmptable]
align 4
mixcodel0:
I=0
rept 32
CODESTART=$
mov bl,[esi]
add dl,dh
movsx eax,[byte ebx]
adc esi,ebp
OPCODE [edi+I],eax
CODELEN=$-CODESTART
I=I+4
endm
add edi,4*32
dec ecx
jge mixcodel0
pop ebx
mov [ebx+voicepos],esi
mov [byte ebx+3+voicefrac],dl
pop esi
pop ebp
pop edx
pop ecx
pop ebx
pop eax
ret
align 4
label mixjmptable dword
I=CODESTART+CODELEN
rept 32
dd I
I=I-CODELEN
endm
endm
test ebx,ebx
je mixvoicef0
mixcode add
mixvoicef0:
mixcode mov
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixvoices - mixes all the voices
; In:
; EDI = buffer address
; ECX = number of samples
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixvoices:
pushad
xor ebx,ebx
mixvoicesl0:
push ebx
push ecx
push edi
shl ebx,2
mov ebp,ecx
mixvoicesl1:
mov ecx,ebp
mov edx,[ebx+voicepos]
mov eax,[ebx+voiceend]
cmp edx,eax
jb mixvoicesf0
sub edx,eax
add edx,[ebx+voiceloop]
cmp edx,eax
jae mixvoicesc0
mov [ebx+voicepos],edx
mixvoicesf0:
sub eax,edx
shl eax,16
mov edx,[ebx+voicefrac]
shr edx,16
sub eax,edx
mov edx,ecx
mov esi,[ebx+voicepitch]
imul edx,esi
cmp edx,eax
jbe mixvoicesf1
dec eax
xor edx,edx
add eax,esi
adc edx,edx
div esi
mov ecx,eax
mixvoicesf1:
call mixvoice
sub ebp,ecx
jg mixvoicesl1
pop edi
pop ecx
pop ebx
inc ebx
cmp bx,[numvoices]
jb mixvoicesl0
popad
ret
mixvoicesc0:
test ebx,ebx
jne mixvoicesc1
push es
mov ax,ds
mov es,ax
xor eax,eax
cld
rep stosd
pop es
mixvoicesc1:
pop edi
pop ecx
pop ebx
inc ebx
cmp bx,[numvoices]
jb mixvoicesl0
popad
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; mixpoll - updates the output buffer
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
mixpoll:
pushad
; get the 32 bit mixing buffer address
mov ecx,DMABUFLEN/2
mov edi,[mixbuffer]
; check if we can fill the current half buffer with samples
call dmagetpos
cmp eax,ecx
jae mixpollf3
cmp [bufoff],ecx
je mixpollf4
jmp mixpolld0
mixpollf3:
cmp [bufoff],ecx
je mixpolld0
; fill the mixing buffer and polls the timer callback routine
mixpollf4:
mov eax,[timeracc]
mov ebp,ecx
mixpolll0:
test eax,eax
jg mixpollf0
call [timerproc]
add eax,[timerspeed]
mixpollf0:
mov ecx,eax
add ecx,63
and cl,not 63
cmp ecx,ebp
jle mixpollf1
mov ecx,ebp
mixpollf1:
call mixvoices
lea edi,[edi+4*ecx]
sub eax,ecx
sub ebp,ecx
jg mixpolll0
mov [timeracc],eax
; translate 32-bit signed samples to 8-bit unsigned samples
mov ecx,DMABUFLEN/2
mov esi,[mixbuffer]
mov edi,[bufptr]
add edi,[bufoff]
xor [bufoff],ecx
mov ebx,[boosttable]
add ebx,1024
shr ecx,4
mixpolll2:
I=0
rept 4
mov eax,[esi+16*I+8]
mov dl,[eax+ebx]
mov eax,[esi+16*I+12]
mov dh,[eax+ebx]
shl edx,16
mov eax,[esi+16*I]
mov dl,[eax+ebx]
mov eax,[esi+16*I+4]
mov dh,[eax+ebx]
mov [edi+4*I],edx
I=I+1
endm
add esi,4*16
add edi,16
dec ecx
jg mixpolll2
mixpolld0:
popad
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster DSP lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbwrite - send a command/data byte to the DSP chip
; In:
; AL = command/data byte
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbwrite:
push eax
push ecx
push edx
mov dx,[ioaddr]
add dx,0Ch
mov ah,al
mov ecx,10000h ; wait until the write buffer
sbwritel0: ; status port (2XCh) bit 7 is clear
in al,dx
and al,80h
loopnz sbwritel0
mov al,ah ; write value in the write
out dx,al ; data port (2XCh)
pop edx
pop ecx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbreset - reset the Sound Blaster DSP chip
; Out:
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbreset:
push eax
push ebx
push ecx
push edx
mov ebx,64 ; try to reset upto 64 times
sbresetl1:
mov dx,[ioaddr]
add dx,06h
mov al,1 ; write 1 to the reset port (2X6h)
out dx,al
xor ah,ah ; wait at least 3 microseconds
sbresetl2:
in al,dx
dec ah
jne sbresetl2
xor al,al ; write 0 to the reset port (2X6h)
out dx,al
add dx,08h
mov ecx,0400h ; wait until the data available
sbresetl0: ; status port (2XEh) bit 7 is set
in al,dx
and al,80h
loopz sbresetl0
sub dx,04h ; read the read data port (2XAh)
in al,dx
cmp al,0AAh
clc
je sbresetd0 ; check the ready byte value
dec ebx
jne sbresetl1
stc
sbresetd0:
pop edx
pop ecx
pop ebx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbsetup - start the DMA output
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbsetup:
push eax
push edx
mov al,0D1h ; turn on the speaker
call sbwrite
mov al,40h ; set the playback rate
call sbwrite
mov ax,1000
mul ax
div [mixfreq]
neg ax
call sbwrite
mov al,14h ; start the lowspeed 8 bit DMA
call sbwrite ; mode transfer to the DAC
mov al,0FFh
call sbwrite
call sbwrite
pop edx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbdone - shut down the DMA output
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbdone:
push eax
call sbreset ; reset the DSP chip
mov al,0D3h
call sbwrite ; turn off the speaker
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; sbdetect - Detect the Sound Blaster I/O Port, IRQ level and DMA channel
; Out:
; DX = I/O port address
; CL = IRQ level
; CH = DMA channel
; CF = status
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
sbdetect:
mov dx,210h ; scan the ports 210h..260h
sbdetectl0:
mov [ioaddr],dx ; check if there is a SB card
call sbreset ; trying to reset the DSP chip
jnc sbdetectf0
add dx,10h
cmp dx,260h
jbe sbdetectl0
xor dx,dx
xor cx,cx
stc
ret
sbdetectf0:
push eax
push ebx
push ecx
push es
mov [datasel],ds
irp I,<2,3,5,7,10> ; install IRQ traps
push cs
pop es
lea ebx,[irqtest&I]
mov cx,I
call irqsetvect
call irqsetmask
push ebx
push es
endm
mov [irqnum],0
mov al,0F2h ; ask to the DSP to raise a IRQ
call sbwrite
mov ecx,10000h ; wait until some IRQ occurs
sbdetectl1:
cmp [irqnum],0
loope sbdetectl1
irp I,<10,7,5,3,2> ; deinstall IRQ traps
pop es
pop ebx
mov cx,0100h+I
call irqsetmask
call irqsetvect
endm
pop es
pop ecx
pop ebx
pop eax
mov dx,[ioaddr] ; return the SB parameters
mov cl,[irqnum]
xor ch,ch
sub ch,cl
mov ch,1
cmc
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster DMA lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmasetup - setup the DMA buffer parameters
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmasetup:
push eax
push ebx
push ecx
push edx
mov bl,[drqnum]
mov al,bl
or al,04h ; reset the DMA channel
out 0Ah,al
out 0Ch,al ; clear the flip flop
mov al,bl
or al,58h ; set the autoinit mode
out 0Bh,al
movzx dx,bl
add dx,dx
mov eax,[bufptr] ; set the buffer address
out dx,al
mov al,ah
out dx,al
inc dx
mov ax,DMABUFLEN ; set the buffer length
dec ax
out dx,al
mov al,ah
out dx,al
mov edx,82818387h ; set the buffer page
mov cl,bl
shl cl,3
shr edx,cl
xor dh,dh
shr eax,16
out dx,al
mov al,bl ; unlock the DMA channel
out 0Ah,al
pop edx
pop ecx
pop ebx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmadone - shut down the DMA controller
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmadone:
push eax
mov al,[drqnum] ; reset the DMA channel
or al,04h
out 0Ah,al
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; dmagetpos - return the DMA buffer relative position
; Out:
; EAX = buffer relative position
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dmagetpos:
push ecx
push edx
out 0Ch,al ; clear the flip flop
mov dl,[drqnum]
xor dh,dh
add dl,dl
inc dl
in al,dx ; read the DMA counter
mov ah,al
in al,dx
xchg al,ah
dmagetposl0:
mov cx,ax ; read again the DMA counter
in al,dx
mov ah,al
in al,dx
xchg al,ah
sub cx,ax
cmp cx,+16 ; both values are near?
jg dmagetposl0 ; nope, try again
cmp cx,-16
jl dmagetposl0
movzx eax,ax ; get the position relative
neg eax ; to the start of the buffer
add eax,DMABUFLEN
pop edx
pop ecx
ret
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
; Sound Blaster IRQ lowlevel stuff
;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetvect - set the IRQ handler routine
; In:
; ES:EBX = IRQ handler routine address
; CL = IRQ level
; Out:
; ES:EBX = previous IRQ handler address
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetvect:
push eax
push ecx
push edx
; get the PIC interrupt master and slave base address
push ebx
push ecx
mov ax,0400h
int 31h
pop ecx
pop ebx
; get the IDT interrupt slot number for the IRQ number
mov al,cl
cmp al,08h
jb irqsetvectf0
mov dh,dl
sub al,08h
irqsetvectf0:
add al,dh
; saves and change the IRQ handler routine
push ds
push es
push ebx
mov ah,35h
int 21h
pop edx
pop ds
push es
push ebx
mov ah,25h
int 21h
pop ebx
pop es
pop ds
pop edx
pop ecx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetmask - enable or disable the IRQ in the interrupt mask registers
; In:
; CL = IRQ level
; CH = enable (=0) or disable (=1)
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetmask:
push eax
push edx
in al,0A1h ; enable or disable the specified
mov ah,al ; IRQ using the PIC interrupt
in al,21h ; mask registers
mov dx,1
shl dx,cl
not dx
and ax,dx
mov dl,ch
shl dx,cl
or ax,dx
out 21h,al
mov al,ah
out 0A1h,al
pop edx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqsetup - install the IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqsetup:
push eax
push ebx
push ecx
; setup DS data selector used by the interrupt handler
mov [datasel],ds
; set the IRQ handler routine and saves the previous vector
push es
mov ax,cs
mov es,ax
lea ebx,[irqhandler]
mov cl,[irqnum]
call irqsetvect
mov [oldirqoff],ebx
mov [oldirqsel],es
pop es
; enable the IRQ signals in the PIC interrupt mask
mov cl,[irqnum]
mov ch,0
call irqsetmask
pop ecx
pop ebx
pop eax
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqdone - restores the old IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqdone:
push ebx
push ecx
; disable IRQ signals in the PIC interrupt mask register
mov cl,[irqnum]
mov ch,1
call irqsetmask
; restore the old IRQ handler routine
push es
mov ebx,[oldirqoff]
mov es,[oldirqsel]
mov cl,[irqnum]
call irqsetvect
pop es
pop ecx
pop ebx
ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqhandler - hardware IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqhandler:
push eax
push edx
push ds
mov ds,[cs:datasel]
; send acknowledge to the PIC controller
mov al,20h
cmp [irqnum],08h
jb irqhandlerf0
out 0A0h,al
irqhandlerf0:
out 20h,al
; send acknowledge to the DSP chip reading the 8 bit ack port (2XEh)
mov dx,[ioaddr]
add dx,0Eh
in al,dx
; restart the 8 bit DMA mode playback transfer
mov al,14h
call sbwrite
mov al,0FFh
call sbwrite
call sbwrite
pop ds
pop edx
pop eax
iretd
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; irqtest - testing hardware IRQ handler routine
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
irqtest:
push edx ; common IRQ test handler code
push ds ; used for autodetection
mov ds,[cs:datasel]
mov [irqnum],al ; save the IRQ level number
mov dx,[ioaddr] ; send acknowledge signal to the
add dx,0Eh ; DSP reading the ack port (2XEh)
in al,dx
mov al,20h ; send acknowledge to the PIC
cmp [irqnum],08h ; controllers
jb irqtestf0
out 0A0h,al
irqtestf0:
out 20h,al
pop ds
pop edx
pop eax
iretd
irp I,<2,3,5,7,10> ; IRQ test handlers for each
irqtest&I: ; possible IRQ levels
push eax
mov ax,I
jmp irqtest
endm
end